#include "AReVi/arSystem.h"
#include "AReVi/Utils/memoryBlock.h"
using namespace AReVi;

#include "connector.h"
#include "fifo.h"
#include "event.h"

#define BUFFERSIZE 4096

/******************************************
 * Connector : Constructeur / Destructeur *
 ******************************************/

AR_CLASS_NOVOID_DEF(Connector, ArObject)

Connector::Connector(ArCW & arCW) : ArObject(arCW), _disconnectCB(thisRef()) {
    // On s'enregistre pour le polling
    Scheduler::access()->addPollingCB(thisRef(), &Connector::_onPolling);
}

Connector::~Connector() {
}


/**********************************
 * Connector : Gestion des socket *
 **********************************/

void Connector::_listenEventsOnSocket(int socket) {
    if (socket < 0) {
        cerr << "internal: Bad socket" << endl;
    }
    else {
    	// Est-ce que la socket est déjà enregistrée
		if (_receiveStack.find(socket) != _receiveStack.end()) return;

        // On passe la socket en non bloquant
        if (ArSystem::isBlockingFD(socket) && ! ArSystem::setBlockingFD(socket, false)) {
            cerr << "internal: Blocked socket" << endl;
        }
        else {
            _receiveStack[socket] = FIFO::NEW();
            ArSystem::addReadFDPolling(socket);
        }
    }
}

void Connector::_sendEventOnSocket(int socket, ArRef<Event> evt) {
cerr << "DEBUG: Sending event [" << evt->_emitterID << "->" << evt->_type << "]" << endl;
    if (socket < 0) {
        cerr << "internal: Bad socket" << endl;
    }
    else {
		ArRef<FIFO> stack;
		StlMap<int, ArRef<FIFO> >::iterator it = _sendStack.find(socket);
		if (it == _sendStack.end()) {
		    // On passe la socket en non bloquant
		    if (ArSystem::isBlockingFD(socket) && ! ArSystem::setBlockingFD(socket, false)) {
		        cerr << "internal: Blocked socket" << endl;
		    }
			else {
				stack = FIFO::NEW();
				_sendStack[socket] = stack;
	            ArSystem::addWriteFDPolling(socket);
			}
		}
		else {
			stack = it->second;
		}

		// On ajoute l'event aux données à envoyer
		if (stack) evt->appendToStack(stack);
    }
}


/*************************
 * Connector : onPolling *
 *************************/

void Connector::_onPolling(const Scheduler::PollingEvent &) {
    char buffer[BUFFERSIZE];
	unsigned int size;

    StlMap<int, ArRef<FIFO> >::iterator it, it2;
    // D'abord on regarde s'il y a quelque chose reçu
    it = _receiveStack.begin();
    while (it != _receiveStack.end()) {
        int socket = it->first;
        ArRef<FIFO> stack = it->second;
        
        if (ArSystem::hasReadFDPolling(socket)) {
			// Lecture des données
            size = ArSystem::partialReadFD(socket, buffer, BUFFERSIZE);

            switch(size) {
            case -1:
                cerr << "error: Error while retreiving data" << endl;
                break;
            case 0:
                if (! _disconnectCB.empty()) {
                    Connector::DisconnectEvent d;
                    d.socket = socket;
                    _disconnectCB.fire(d);
                }
                _receiveStack.erase(it);
				ArSystem::removeReadFDPolling(socket);
				
				it2 = _sendStack.find(socket);
				if (it2 != _sendStack.end()) {
					_sendStack.erase(it2);
					ArSystem::removeWriteFDPolling(socket);
				}
				break;
            case -2:
                // Rien à faire, on passe à la suite
                break;
            default:
				// On ajoute les données à la pile
				stack->append(size, buffer);
				
				// Tant qu'on peut, on essaye de créer des Event
				while (stack->getSize() > 0) {
					ArRef<Event> evt = Event::createFromStack(stack);
					if (evt == Event::nullRef()) break;
					treatEvent(socket, evt);
					// Lancer les callbacks
					fireCallbacks(evt);
				}
                break;
            }
            it++;
        }
    }

    // Ensuite on regarde s'il y a des données à envoyer
    it = _sendStack.begin();
    while (it != _sendStack.end()) {
        int socket = it->first;
        ArRef<FIFO> stack = it->second;
        
        if (ArSystem::hasWriteFDPolling(socket)) {
			// Lecture des données
            size = ArSystem::partialWriteFD(socket, stack->access(), stack->getSize());

            switch(size) {
            case -1:
                cerr << "error: Error while sending data" << endl;
				break;
            case -2:
                // Rien à faire, on passe à la suite
                break;
            default:
				// On ajoute les données à la pile
				if (size) stack->unstack(size);
                // On passe au suivant
				if (stack->getSize() == 0) {
					ArSystem::removeWriteFDPolling(socket);
					_sendStack.erase(it);
				}
                break;
            }
			it++;
        }
    }
}



void Connector::treatEvent(int, ArRef<Event>) {
}

void Connector::fireCallbacks(ArRef<Event> evt) {
cerr << "DEBUG: Received event [" << evt->_emitterID << "->" << evt->_type << "] matchs :" << endl;
	StlVector<iCallback *>::iterator it = _callbacks.begin();
	while (it != _callbacks.end()) {
		// Un peu de ménage au cas où
		if (! (*it)->object) {
			delete (*it);
			_callbacks.erase(it);
			continue;
		}
    it++;
  }
  for(uint i=0; i<_callbacks.size(); i++)
  {
		// Si nécessaire, on appelle la méthode
		if (((_callbacks[i])->anyEmitter || (_callbacks[i])->emitterID == evt->_emitterID) && ((_callbacks[i])->anyType || (_callbacks[i])->type == evt->_type)) {
cerr << "DEBUG: - [" << (((_callbacks[i])->anyEmitter)? "*" : (_callbacks[i])->emitterID) << "->" << (((_callbacks[i])->anyType)? "*" : (_callbacks[i])->type) << "]" << endl;
			((_callbacks[i])->object.c_ptr()->*(_callbacks[i])->method)(evt);
		}
	}
}

/*************************
 * Le connecteur serveur *
 *************************/

AR_CLASS_NOVOID_DEF(ConnectorServer, Connector)

ConnectorServer::ConnectorServer(ArCW & arCW, int port) : Connector(arCW) {
    /** Initialisation de la socket pour l'écoute */
    _listenSocket = ArSystem::listenTCP(port);
    if (_listenSocket < 0) {
        cerr << "error: Unable to listen on port " << port << endl;
    }
    else {
        if (! ArSystem::setBlockingFD(_listenSocket, false)) {
            cerr << "error: (internal) setBlockingFD(_listenSocket, false)" << endl;
        }
        else {
            ArSystem::addReadFDPolling(_listenSocket);
        }
    }
    
    /** Pour gérer les déconnections */
    addDisconnectCB(thisRef(), &ConnectorServer::_onDisconnect);
    
    /** Enregistrement de la callback pour la réception d'évènements */
    Scheduler::access()->addPollingCB(thisRef(), &ConnectorServer::_onPolling);
}

ConnectorServer::~ConnectorServer() {
	// On ferme toutes les connexions proprement
	// TODO ...
}

void ConnectorServer::_onPolling(const Scheduler::PollingEvent &) {
    if (ArSystem::hasReadFDPolling(_listenSocket)) {
        unsigned int ip;
        int port;
        int socket;
        socket = ArSystem::acceptTCP(_listenSocket, ip, port);
        if (socket >= 0) {
            // On génère l'id suivante
            _lastID++;
            // On envoi l'ID en bloquant
            unsigned int size = sizeof(unsigned int);
            char * data = (char *)&_lastID;
            while (size > 0) {
                int sent = ArSystem::writeFD(socket, (void *)data, size);
                if (sent == -1) {
                    cerr << "error: While sending unique ID to client" << endl;
                    ArSystem::closeFD(socket);
                    return;
                }
                size -= sent;
                data += sent;
            }
            ArSystem::syncFD(socket);
            // On enregistre la socket et on écoute les évènements
            _clientSockets.push_back(socket);
			_listenEventsOnSocket(socket);
        }
		else {
			if (socket != -2) cerr << "error: While accepting connection" << endl;
		}
    }
}

void ConnectorServer::_onDisconnect(const Connector::DisconnectEvent & evt) {
	StlVector<int>::iterator it = _clientSockets.begin();
	while (it != _clientSockets.end()){
		if (*it == evt.socket) _clientSockets.erase(it);
		else it++;
	}
}


void ConnectorServer::broadcast(ArRef<Event> evt) {
	StlVector<int>::iterator it = _clientSockets.begin();
	while (it != _clientSockets.end()) {
		_sendEventOnSocket(*it++, evt);
	}
}

void ConnectorServer::treatEvent(int socketFrom, ArRef<Event> evt) {
	StlVector<int>::iterator it = _clientSockets.begin();
	while (it != _clientSockets.end()) {
		if (*it != socketFrom) _sendEventOnSocket(*it, evt);
		it++;
	}
}

/************************
 * Le connecteur client *
 ************************/

AR_CLASS_NOVOID_DEF(ConnectorClient, Connector)

ConnectorClient::ConnectorClient(ArCW & arCW, StlString hostname, int port) : Connector(arCW) {
    // Connexion au serveur
    unsigned int ip = ArSystem::getIPAddress(hostname);
    if (ip == 0) {
        cerr << "error: Unable to resolve hostname : " << hostname << endl;
    }
    else {
        _socket = ArSystem::connectTCP(ip, port);
        if (_socket < 0) {
            cerr << "error: Can't connect to " << hostname << " on port " << port << endl;
        }
        else {
            // On récupère l'ID unique
            unsigned int id = 0;
            unsigned int size = sizeof(unsigned int);
            char *data = (char *)&id;
            while (size > 0) {
                int received = ArSystem::readFD(_socket, (void *) data, size);
                if (received == -1) {
                    cerr << "error: While retriving unique ID" << endl;
                    return;
                }
                size -= received;
                data += received;
            }
            _id = intToStr(id);
            // On commence à écouter les évènements
			_listenEventsOnSocket(_socket);
        }
    }
}

ConnectorClient::~ConnectorClient() {
}

void ConnectorClient::broadcast(ArRef<Event> evt) {
	_sendEventOnSocket(_socket, evt);
}

